﻿using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Face8280WebSocketServer.WebSocket
{
    public class WebsocketMiddlewareBase
    {
        public ILogger _logger;

        public class SocketContext
        {
            public System.Net.WebSockets.WebSocket CurrentSocket;
            public CancellationToken SocketCancellationToken;
            public string WebsocketID;
        }


        public async Task<SocketContext> ConnectClient(HttpContext context)
        {
            SocketContext socket = new SocketContext();
            socket.WebsocketID = Guid.NewGuid().ToString();

            socket.SocketCancellationToken = context.RequestAborted;
            socket.CurrentSocket = await context.WebSockets.AcceptWebSocketAsync();
            return socket;
        }

        public async Task Run(SocketContext socket, Func<SocketContext, string, Task> actionReceive)
        {
            while (true)
            {
                if (socket.SocketCancellationToken.IsCancellationRequested)
                {
                    break;
                }
                try
                {
                    if (socket.CurrentSocket == null) break;
                    string response = await ReceiveStringAsync(socket);
                    if (socket.CurrentSocket == null) break;
                    if (socket.CurrentSocket.State != WebSocketState.Open)
                    {
                        break;
                    }
                    if (string.IsNullOrEmpty(response))
                    {
                        continue;
                    }
                    else
                    {
                        if (response.StartsWith("PING"))
                        {
                            await SendStringAsync(socket.CurrentSocket, "PONG", socket.SocketCancellationToken);
                            continue;
                        }
                        else
                        {
                            await actionReceive(socket, response);
                        }
                    }
                }
                catch (Exception ex)
                {
                    _logger.LogError("WebsocketMiddlewareBase.Run -- " + ex.Message);
                    continue;
                }

            }
            await CloseConnect(socket);
        }


        public Task SendStringAsync(System.Net.WebSockets.WebSocket socket, string data, CancellationToken ct = default(CancellationToken))
        {
            var buffer = Encoding.UTF8.GetBytes(data);
            var segment = new ArraySegment<byte>(buffer);
            return socket.SendAsync(segment, WebSocketMessageType.Text, true, ct);
        }

        public async Task<string> ReceiveStringAsync(SocketContext socket)
        {
            var buffer = new ArraySegment<byte>(new byte[8192]);
            using (var ms = new MemoryStream())
            {
                WebSocketReceiveResult result = null;
                try
                {
                    do
                    {
                        socket.SocketCancellationToken.ThrowIfCancellationRequested();
                        if (socket.CurrentSocket == null) break;
                        result = await socket.CurrentSocket.ReceiveAsync(buffer, socket.SocketCancellationToken);
                        ms.Write(buffer.Array, buffer.Offset, result.Count);
                    }
                    while (!result.EndOfMessage);
                }
                catch (Exception ex)
                {
                    _logger.LogError("WebsocketMiddlewareBase.ReceiveStringAsync -- " +  ex.Message);
                    return string.Empty;
                }

                if (result == null)
                {
                    return string.Empty;
                }
                ms.Seek(0, SeekOrigin.Begin);
                if (result.MessageType != WebSocketMessageType.Text)
                {
                    return null;
                }

                using (var reader = new StreamReader(ms, Encoding.UTF8))
                {
                    return await reader.ReadToEndAsync();
                }
            }
        }

        public async Task CloseConnect(SocketContext socket)
        {
            if (socket.CurrentSocket == null) return;

            try
            {
                if (socket.CurrentSocket.State != WebSocketState.Aborted)
                {
                    await socket.CurrentSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", socket.SocketCancellationToken);
                    socket.CurrentSocket.Dispose();
                }
            }
            catch (Exception)
            {

                _logger.LogError("WebsocketMiddlewareBase.CloseConnect -- " + "关闭 websocket 连接时发生错误"); ;
            }
            socket.CurrentSocket = null;
        }

    }
}
